	.Title	"RTC"
;
; Program:	rtc.asm
; Author:		Andrew Lynch
; Date:		22 Feb 2007
; Enviroment:	TASM MS-DOS Z80 Cross Assembler source for CP/M
;

; constants

mask_data:	.EQU	$80		; RTC data line
mask_clk:	.EQU	$40		; RTC Serial Clock line
mask_rd:	.EQU	$20		; Enable data read from RTC
mask_rst:	.EQU	$10		; De-activate RTC reset line

RTC:		.EQU	$70		; Base address for DS1302 RTC

	.ORG	0100H

; print message 16 times
Print:
	LD	HL,$0010
	PUSH	HL
LOOP:
	LD	DE,MSG
	LD	C,09H			; CP/M write string to console call
	CALL	0005H

	POP	HL			; Get loop counter
	DEC	HL			;   and decrement it

	PUSH	HL			; Put back on stack

;	LD	A,L
	LD	A,H			; added
	CP	$00			; added

	JR	NZ,LOOP		; Loop while not zero

	POP	HL			; Clean up stack

;	program starts here

	CALL	RTC_TOP_LOOP
 
;	RET	

;
;	LD	A,$FF
;	OUT	(RTC),A

;	JP	$f803	; MONITOR WARM START

	LD	C,00H			; CP/M system reset call - shut down
	CALL	0005H
		
	HALT				; This code is never reached


; function HEXSTR
; input number in A
; output upper nibble of number in ASCII in H
; output lower nibble of number in ASCII in L
; uses BC
;
; based on following algorithm:
:
;  const
;    hextab : string = ('0','1','2','3','4','5','6','7','8',
;                       '9','A','B','C','D','E','F');
;
;  PROCEDURE hexstr(n: int): ^string;
;  BEGIN
;    n := n and 255;
;    tmpstr[1] := hextab[n / 16];
;    tmpstr[2] := hextab[n and 15];
;    tmpstr[0] := #2;
;    return @tmpstr;
;  END;


HEXSTR:
	PUSH	BC			;SAVE BC
	LD	B,A
	RLC	A			;DO HIGH NIBBLE FIRST  
	RLC	A
	RLC	A
	RLC	A
	AND	0FH			;ONLY THIS NOW
	ADD	A,30H			;TRY A NUMBER
	CP	3AH			;TEST IT
	JR	C,HEXSTR1		;IF CY SET SAVE 'NUMBER' in H
	ADD	A,07H			;MAKE IT AN ALPHA
HEXSTR1:
	LD	H,A			;SAVE 'ALPHA' in H
	LD	A,B			;NEXT NIBBLE
	AND	0FH			;JUST THIS
	ADD	A,30H			;TRY A NUMBER
	CP	3AH			;TEST IT
	JR	C,HEXSTR2		;IF CY SET SAVE 'NUMBER' in L
	ADD	A,07H			;MAKE IT ALPHA

HEXSTR2:
	LD	L,A			;SAVE 'ALPHA' in L
	POP	BC			;RESTORE BC
	RET


;*****************************************************
;*	GET K.B. DATA & MAKE IT 'HEX'
;*****************************************************

HEXIN:
	PUSH	BC		;SAVE BC REGS.
	CALL	NIBL		;DO A NIBBLE
	RLC	A		;MOVE FIRST BYTE UPPER NIBBLE  
	RLC	A
	RLC	A
	RLC	A
	LD	B,A		;SAVE ROTATED BYTE
				;DO NEXT NIBBLE
	LD	C,01H		; CP/M console input call
	CALL	0005H		;GET K.B. DATA
	ADD	A,B		;COMBINE NIBBLES IN ACC.
	POP	BC		;RESTORE BC
	RET			;DONE  
NIBL:
	LD	C,01H		; CP/M console input call
	CALL	0005H		;GET K.B. DATA
	CP	40H		;TEST FOR ALPHA
	JR	NC,ALPH
	AND	0FH		;GET THE BITS
	RET
ALPH:
	AND	0FH		;GET THE BITS
	ADD	A,09H		;MAKE IT HEX A-F
	RET


; function RTC_BIT_DELAY
;
; based on following algorithm:
;
;  { Make a short delay }
;  PROCEDURE rtc_bit_delay;
;   var
;     x : int;
;  BEGIN
;    x := 3;
;  END;

RTC_BIT_DELAY:				; purpose is to delay ~36 uS or 144 t-states at 4MHz
	PUSH	AF			; 11 t-states
	LD	A,07H			; 7 t-states ADJUST THE TIME 13h IS FOR 4 MHZ
RTC_BIT_DELAY1:
	DEC	A			; 4 t-states DEC COUNTER. 4 T-states = 1 uS.
	JP	NZ,RTC_BIT_DELAY1	; 10 t-states JUMP TO PAUSELOOP2 IF A <> 0.

	NOP				; 4 t-states
	NOP				; 4 t-states
	POP	AF			; 10 t-states
	RET				; 10 t-states (144 t-states total)


; function RTC_RESET
;
; based on following algorithm:
;
;  { Output a RTC reset signal }
;  PROCEDURE rtc_reset;
;  BEGIN
;    out(rtc_base,mask_data + mask_rd);
;    rtc_bit_delay();
;    rtc_bit_delay();
;    out(rtc_base,mask_data + mask_rd + mask_rst);
;    rtc_bit_delay();
;    rtc_bit_delay();
;  END;
;
RTC_RESET:
	LD	A,mask_data + mask_rd
	OUT	(RTC),A
	CALL	RTC_BIT_DELAY
	CALL	RTC_BIT_DELAY
	LD	A,mask_data + mask_rd + mask_rst
	OUT	(RTC),A
	CALL	RTC_BIT_DELAY
	CALL	RTC_BIT_DELAY
	RET


; function RTC_RESET_ON
;
; based on following algorithm:
;
;  { Assert RTC reset signal }
;  PROCEDURE rtc_reset_on;
;  BEGIN
;    out(rtc_base,mask_data + mask_rd);
;    rtc_bit_delay();
;    rtc_bit_delay();
;  END;

RTC_RESET_ON:
	LD	A,mask_data + mask_rd
	OUT	(RTC),A
	CALL	RTC_BIT_DELAY
	CALL	RTC_BIT_DELAY
	RET

; function RTC_RESET_OFF
;
; based on following algorithm:
;
;  { De-assert RTC reset signal }
;  PROCEDURE rtc_reset_off;
;  BEGIN
;    out(rtc_base,mask_data + mask_rd + mask_rst);
;    rtc_bit_delay();
;    rtc_bit_delay();
;  END;

RTC_RESET_OFF:
	LD	A,mask_data + mask_rd + mask_rst
	OUT	(RTC),A
	CALL	RTC_BIT_DELAY
	CALL	RTC_BIT_DELAY
	RET

; function RTC_WR
; input value in C
; uses A
;
;  PROCEDURE rtc_wr(n : int);
;   var
;    i : int;
;  BEGIN
;    for i := 0 while i < 8 do inc(i) loop
;       if (n and 1) <> 0 then
;          out(rtc_base,mask_rst + mask_data);
;          rtc_bit_delay();
;          out(rtc_base,mask_rst + mask_clk + mask_data);
;       else
;          out(rtc_base,mask_rst);
;          rtc_bit_delay();
;          out(rtc_base,mask_rst + mask_clk);
;       end;
;       rtc_bit_delay();
;       n := shr(n,1);
;    end loop;
;  END;

RTC_WR:
	XOR	A			; set A=0 index counter of FOR loop

RTC_WR1:
	PUSH	AF			; save accumulator as it is the index counter in FOR loop
	LD	A,C			; get the value to be written in A from C (passed value to write in C)
	BIT	0,A			; is LSB a 0 or 1?
	JP	Z,RTC_WR2		; if it’s a 0, handle it at RTC_WR2.
					; LSB is a 1, handle it below
					; setup RTC latch with RST and DATA high, SCLK low
	LD	A,mask_rst + mask_data
	OUT	(RTC),A		; output to RTC latch
	CALL	RTC_BIT_DELAY	; let it settle a while
					; setup RTC with RST, DATA, and SCLK high
	LD	A,mask_rst + mask_clk + mask_data
	OUT	(RTC),A		; output to RTC latch
	JP	RTC_WR3		; exit FOR loop 

RTC_WR2:
					; LSB is a 0, handle it below
	LD	A,mask_rst		; setup RTC latch with RST high, SCLK and DATA low
	OUT	(RTC),A		; output to RTC latch
	CALL	RTC_BIT_DELAY	; let it settle a while
					; setup RTC with RST and SCLK high, DATA low
	LD	A,mask_rst + mask_clk
	OUT	(RTC),A		; output to RTC latch

RTC_WR3:
	CALL	RTC_BIT_DELAY	; let it settle a while
	RRC	C			; move next bit into LSB position for processing to RTC
	POP	AF			; recover accumulator as it is the index counter in FOR loop
	INC	A			; increment A in FOR loop (A=A+1)
	CP	$08			; is A < $08 ?
	JP	NZ,RTC_WR1		; No, do FOR loop again
	RET				; Yes, end function and return


; function RTC_RD
; output value in C
; uses A
;
; function RTC_RD
;
;  PROCEDURE rtc_rd(): int ;
;   var
;     i,n,mask : int;
;  BEGIN
;    n := 0;
;    mask := 1;
;    for i := 0 while i < 8 do inc(i) loop
;       out(rtc_base,mask_rst + mask_rd);
;       rtc_bit_delay();
;       if (in(rtc_base) and #1) <> #0 then
;          { Data = 1 }
;          n := n + mask;
;       else
;          { Data = 0 }
;       end;
;       mask := shl(mask,1);
;       out(rtc_base,mask_rst + mask_clk + mask_rd);
;       rtc_bit_delay();
;    end loop;
;    return n;
;  END;

RTC_RD:
	XOR	A			; set A=0 index counter of FOR loop
	LD	C,$00			; set C=0 output of RTC_RD is passed in C
	LD	B,$01			; B is mask value

RTC_RD1:
	PUSH	AF			; save accumulator as it is the index counter in FOR loop
					; setup RTC with RST and RD high, SCLK low
	LD	A,mask_rst + mask_rd
	OUT	(RTC),A		; output to RTC latch
	CALL	RTC_BIT_DELAY	; let it settle a while
	IN	A,(RTC)		; input from RTC latch
	BIT	0,A			; is LSB a 0 or 1?
	JP	Z,RTC_RD2		; if LSB is a 1, handle it below
	LD	A,C
	ADD	A,B
	LD	C,A
;	INC	C
					; if LSB is a 0, skip it (C=C+0)
RTC_RD2:
	RLC	B			; move input bit out of LSB position to save it in C
					; setup RTC with RST, SCLK high, and RD high
	LD	A,mask_rst + mask_clk + mask_rd
	OUT	(RTC),A		; output to RTC latch
	CALL	RTC_BIT_DELAY	; let it settle
	POP	AF			; recover accumulator as it is the index counter in FOR loop
	INC	A			; increment A in FOR loop (A=A+1)
	CP	$08			; is A < $08 ?
	JP	NZ,RTC_RD1		; No, do FOR loop again
	RET				; Yes, end function and return.  Read RTC value is in C

; function RTC_WRITE
; input address in D
; input value in E
; uses A
;
; based on following algorithm:		
;
;  PROCEDURE rtc_write(address, value: int);
;  BEGIN
;    lock();
;    rtc_reset_off();
;    { Write command }
;    rtc_wr(128 + shl(address and $3f,1));
;    { Write data }
;    rtc_wr(value and $ff);
;    rtc_reset_on();
;    unlock();
;  END;

RTC_WRITE:
	DI				; disable interrupts during critical section
	CALL	RTC_RESET_OFF	; turn off RTC reset
	LD	A,D			; bring into A the address from D
;	AND	$3F			; keep only bits 6 LSBs, discard 2 MSBs
	AND	%00111111		; keep only bits 6 LSBs, discard 2 MSBs
	RLC	A			; rotate address bits to the left
;	ADD	A,$80			; set MSB to one for DS1302 COMMAND BYTE (WRITE)
	ADD	A,%10000000		; set MSB to one for DS1302 COMMAND BYTE (WRITE)
	LD	C,A			; RTC_WR expects write data (address) in reg C
	CALL	RTC_WR		; write address to DS1302
	LD	A,E			; start processing value
	AND	$FF			; seems unnecessary, probably delete since all values are 8-bit
	LD	C,A			; RTC_WR expects write data (value) in reg C
	CALL	RTC_WR		; write address to DS1302
	CALL	RTC_RESET_ON	; turn on RTC reset
	EI
	RET


; function RTC_READ
; input address in D
; output value in C
; uses A
;
; based on following algorithm
;
;  PROCEDURE rtc_read(address: int): int;
;   var
;     n : int;
;  BEGIN
;    lock();
;    rtc_reset_off();
;    { Write command }
;    rtc_wr(128 + shl(address and $3f,1) + 1);
;    { Read data }
;    n := rtc_rd();
;    rtc_reset_on();
;    unlock();
;    return n;
;  END;

RTC_READ:
	DI				; disable interrupts during critical section
	CALL	RTC_RESET_OFF	; turn off RTC reset
	LD	A,D			; bring into A the address from D
;	AND	$3F			; keep only bits 6 LSBs, discard 2 MSBs
	AND	%00111111		; keep only bits 6 LSBs, discard 2 MSBs
	RLC	A			; rotate address bits to the left
;	ADD	A,$81			; set MSB to one for DS1302 COMMAND BYTE (READ)
	ADD	A,%10000001		; set MSB to one for DS1302 COMMAND BYTE (READ)
	LD	C,A			; RTC_WR expects write data (address) in reg C
	CALL	RTC_WR		; write address to DS1302
	CALL	RTC_RD		; read value from DS1302 (value is in reg C)
	CALL	RTC_RESET_ON	; turn on RTC reset
	EI
	RET


; function RTC_WR_PROTECT
; input D (address) $07
; input E (value) $80
; uses A
;
; based on following algorithm
;
;  PROCEDURE rtc_wr_protect;
;  BEGIN
;    rtc_write(7,128);
;  END;

RTC_WR_PROTECT:
;	LD	D,$07
	LD	D,%00000111
;	LD	E,$80
	LD	E,%10000000
	CALL	RTC_WRITE
	RET


; function RTC_WR_UNPROTECT
; input D (address) $07
; input E (value) $00
; uses A
;
; based on following algorithm
;
;  PROCEDURE rtc_wr_unprotect;
;  BEGIN
;    rtc_write(7,0);
;  END;

RTC_WR_UNPROTECT:
;	LD	D,$07
	LD	D,%00000111
;	LD	E,$00
	LD	E,%00000000
	CALL	RTC_WRITE
	RET


; function RTC_GET_TIME
; input HL (memory address of buffer)
; uses A,C,D,E
;
; based on following algorithm
;
;  PROCEDURE rtc_get_time(var buf: string);
;   var
;     n  : int;
;  BEGIN
;    lock();
;    rtc_reset_off();
;    { Write command, burst read }
;    rtc_wr(255 - 64);
;    { Read seconds }
;    n := rtc_rd(); 0
;    buf[16] := char(((n / 16) and $07)) + '0';
;    buf[17] := char((n and $0f)) + '0';
;    { Read minutes }
;    n := rtc_rd(); 1
;    buf[13] := char(((n / 16) and $07)) + '0';
;    buf[14] := char((n and $0f)) + '0';
;    buf[15] := ':';
;    { Read hours }
;    n := rtc_rd(); 2
;    buf[10] := char(((n / 16) and $03)) + '0';
;    buf[11] := char((n and $0f)) + '0';
;    buf[12] := ':';
;    { Read date }
;    n := rtc_rd(); 3
;    buf[7] := char(((n / 16) and $03)) + '0';
;    buf[8] := char((n and $0f)) + '0';
;    buf[9] := ' ';
;    { Read month }
;    n := rtc_rd(); 4
;    buf[4] := char(((n / 16) and $03)) + '0';
;    buf[5] := char((n and $0f)) + '0';
;    buf[6] := '-';
;    { Read day }
;    n := rtc_rd(); 5
;    {
;    buf[4] := char(((n / 16) and $03)) + '0';
;    buf[4] := char((n and $0f)) + '0';
;    }
;    { Read year }
;    n := rtc_rd(); 6
;    buf[1] := char(((n / 16) and $0f)) + '0';
;    buf[2] := char((n and $0f)) + '0';
;    buf[3] := '-';
;    length(buf) := 17;
;    rtc_reset_on();
;    unlock();
;  END rtc_get_time;

RTC_GET_TIME:
	DI				; disable interrupts during DS1302 read
	CALL	RTC_RESET_OFF		; turn of RTC reset
					;    { Write command, burst read }
	LD	C,%10111111		; (255 - 64)
	CALL	RTC_WR			; send COMMAND BYTE (BURST READ) to DS1302

;    { Read seconds }

	CALL	RTC_RD			; read value from DS1302, value is in Reg C

	; digit 16
	LD	A,C			; put value output in Reg C into accumulator
	RLC	A
	RLC	A
	RLC	A
	RLC	A
	AND	$07
	ADD	A,'0'
	LD	(RTC_PRINT_BUFFER+15),A

	; digit 17
	LD	A,C			; put value output in Reg C into accumulator
	AND	$0F
	ADD	A,'0'
	LD	(RTC_PRINT_BUFFER+16),A

;    { Read minutes }

	CALL	RTC_RD			; read value from DS1302, value is in Reg C

	; digit 13
	LD	A,C			; put value output in Reg C into accumulator
	RLC	A
	RLC	A
	RLC	A
	RLC	A
	AND	$07
	ADD	A,'0'
	LD	(RTC_PRINT_BUFFER+12),A

	; digit 14
	LD	A,C			; put value output in Reg C into accumulator
	AND	$0F
	ADD	A,'0'
	LD	(RTC_PRINT_BUFFER+13),A

	; digit 15	
	LD	A,':'
	LD	(RTC_PRINT_BUFFER+14),A

;    { Read hours }

	CALL	RTC_RD			; read value from DS1302, value is in Reg C

	; digit 10
	LD	A,C			; put value output in Reg C into accumulator
	RLC	A
	RLC	A
	RLC	A
	RLC	A
	AND	$03
	ADD	A,'0'
	LD	(RTC_PRINT_BUFFER+09),A

	; digit 11
	LD	A,C			; put value output in Reg C into accumulator
	AND	$0F
	ADD	A,'0'
	LD	(RTC_PRINT_BUFFER+10),A

	; digit 12
	LD	A,':'
	LD	(RTC_PRINT_BUFFER+11),A

;    { Read date }

	CALL	RTC_RD			; read value from DS1302, value is in Reg C

	; digit 07
	LD	A,C			; put value output in Reg C into accumulator
	RLC	A
	RLC	A
	RLC	A
	RLC	A
	AND	$03
	ADD	A,'0'
	LD	(RTC_PRINT_BUFFER+06),A

	; digit 08
	LD	A,C			; put value output in Reg C into accumulator
	AND	$0F
	ADD	A,'0'
	LD	(RTC_PRINT_BUFFER+07),A

	; digit 09
	LD	A,' '
	LD	(RTC_PRINT_BUFFER+08),A

;    { Read month }

	CALL	RTC_RD			; read value from DS1302, value is in Reg C

	; digit 04
	LD	A,C			; put value output in Reg C into accumulator
	RLC	A
	RLC	A
	RLC	A
	RLC	A
	AND	$03
	ADD	A,'0'
	LD	(RTC_PRINT_BUFFER+03),A

	; digit 05
	LD	A,C			; put value output in Reg C into accumulator
	AND	$0F
	ADD	A,'0'
	LD	(RTC_PRINT_BUFFER+04),A

	; digit 06
	LD	A,'-'
	LD	(RTC_PRINT_BUFFER+05),A

;    { Read day }

	CALL	RTC_RD			; read value from DS1302, value is in Reg C

	; digit 04
;	LD	A,C			; put value output in Reg C into accumulator
;	RLC	A
;	RLC	A
;	RLC	A
;	RLC	A
;	AND	$03
;	ADD	A,'0'
;	LD	(RTC_PRINT_BUFFER+03),A

	; digit 04
;	LD	A,C			; put value output in Reg C into accumulator
;	AND	$0F
;	ADD	A,'0'
;	LD	(RTC_PRINT_BUFFER+03),A

; add special code to put "DAY" value at end of string until better solution known

	; digit 18
	LD	A,'-'
	LD	(RTC_PRINT_BUFFER+17),A

	; digit 19
	LD	A,C			; put value output in Reg C into accumulator
	RLC	A
	RLC	A
	RLC	A
	RLC	A
	AND	$0F
	ADD	A,'0'
	LD	(RTC_PRINT_BUFFER+18),A

	; digit 20
	LD	A,C			; put value output in Reg C into accumulator
	AND	$0F
	ADD	A,'0'
	LD	(RTC_PRINT_BUFFER+19),A




;    { Read year }

	CALL	RTC_RD			; read value from DS1302, value is in Reg C

	; digit 01
	LD	A,C			; put value output in Reg C into accumulator
	RLC	A
	RLC	A
	RLC	A
	RLC	A
	AND	$0F
	ADD	A,'0'
	LD	(RTC_PRINT_BUFFER+00),A

	; digit 02
	LD	A,C			; put value output in Reg C into accumulator
	AND	$0F
	ADD	A,'0'
	LD	(RTC_PRINT_BUFFER+01),A

	; digit 03
	LD	A,'-'
	LD	(RTC_PRINT_BUFFER+02),A

	CALL	RTC_RESET_ON		; turn RTC reset back on 
	EI				; re-enable interrupts

	RET				; Yes, end function and return


; function RTC_SET_NOW
; uses A, D, E
;
; based on following algorithm
;
;  { Set time to 96-02-18 19:43:00 }
;  PROCEDURE rtc_set_now;
;  BEGIN
;    rtc_wr_unprotect();
;    { Set seconds }
;    rtc_write(0,0);
;    { Set minutes }
;    rtc_write(1,$43);
;    { Set hours }
;    rtc_write(2,$19);
;    { Set date }
;    rtc_write(3,$18);
;    { Set month }
;    rtc_write(4,$02);
;    { Set day }
;    rtc_write(5,$07);
;    { Set year }
;    rtc_write(6,$96);
;    rtc_wr_protect();
;  END;

RTC_SET_NOW:
; set time to 07-02-23 19:45:00-05 <-Friday
	CALL RTC_WR_UNPROTECT
; seconds
	LD	D,$00
	LD	E,$00
	CALL RTC_WRITE

; minutes
	LD	D,$01
	LD	E,$45
	CALL RTC_WRITE

; hours
	LD	D,$02
	LD	E,$19
	CALL RTC_WRITE

; date
	LD	D,$03
	LD	E,$23
	CALL RTC_WRITE

; month
	LD	D,$04
	LD	E,$02
	CALL RTC_WRITE

; day
	LD	D,$05
	LD	E,$06
	CALL RTC_WRITE

; year
	LD	D,$06
	LD	E,$07
	CALL RTC_WRITE

	CALL RTC_WR_PROTECT
	RET


; function RTC_RESTART
;
; uses A, D, E,
;
; based on the following algorithm
;
;  { Restart clock, set seconds to 00 }
;  PROCEDURE rtc_restart;
;  BEGIN
;    rtc_wr_unprotect();
;    { Set seconds }
;    rtc_write(0,0);
;    rtc_wr_protect();
;  END;

RTC_RESTART:
	CALL RTC_WR_UNPROTECT
	LD	D,$00
	LD	E,$00
	CALL RTC_WRITE
	CALL RTC_WR_PROTECT
	RET


; function RTC_CHARGE_ENABLE
;
; uses A, D, E
;
; based on following algorithm
;
;  PROCEDURE rtc_charge_enable;
;  BEGIN
;    rtc_wr_unprotect();
;    { Enable trickle charger, 2kohm, 1 diode }
;    rtc_write(8,$a5);
;    rtc_wr_protect();
;  END;

RTC_CHARGE_ENABLE
	CALL	RTC_WR_UNPROTECT
	LD	D,$08
	LD	E,$A5
	CALL	RTC_WRITE
	CALL	RTC_WR_PROTECT
	RET


; function RTC_CHARGE_DISABLE
;
; uses A, D, E
;
; based on following algorithm
;
;  PROCEDURE rtc_charge_disable;
;  BEGIN
;    rtc_wr_unprotect();
;    { Disable trickle charger}
;    rtc_write(8,$00);
;    rtc_wr_protect();
;  END;

RTC_CHARGE_DISABLE
	CALL	RTC_WR_UNPROTECT
	LD	D,$08
	LD	E,$00
	CALL	RTC_WRITE
	CALL	RTC_WR_PROTECT
	RET


; function TEST_BIT_DELAY
;
; based on the following algorithm
;
;
;  PROCEDURE test_bit_delay();
;   var
;     i,t0,t1 : int;
;  BEGIN
;    putln("Testing bit delay...");
;    t0 := sys_time();
;    for i := 0 while i < 1000 do inc(i) loop
;      rtc_bit_delay();
;    end loop;
;    t1 := sys_time();
;    putln(i," rtc_bit_delay calls took ",t1-t0," ms.");
;  END;

RTC_TEST_BIT_DELAY
	LD	DE,TESTING_BIT_DELAY_MSG
	LD	C,09H			; CP/M write string to console call
	CALL	0005H
	LD	C,01H			; CP/M console input call
	CALL	0005H

	; test should take approximately 43 seconds based on the following code analysis
	; of Z80 T-states on a 4 MHz processor
	; =(4+15*(7+255*(7+255*(17+144+4+10)+4+10)+10)+7)/4/1000000

	LD	B,$0F
PAUSE:
	LD	C,$FF
PAUSE1:
	LD	A,$FF			; ADJUST THE TIME 13h IS FOR 4 MHZ
PAUSE2:
	CALL	RTC_BIT_DELAY		; CAUSE 36uS DELAY
	DEC	A			; DEC COUNTER.
	JP	NZ,PAUSE2		; JUMP TO PAUSE2 IF A <> 0.
	DEC	C			; DEC COUNTER
	JP	NZ,PAUSE1		; JUMP TO PAUSE1 IF C <> 0.
	DJNZ	PAUSE			; JUMP TO PAUSE IF B <> 0.

	LD	DE,TESTING_BIT_DELAY_OVER
	LD	C,09H			; CP/M write string to console call
	CALL	0005H
	RET


; function RTC_HELP
;
; based on following algorithm
;
;  PROCEDURE help();
;  BEGIN
;    putln();
;    putln("rtc: ",version);
;    putln("rtc: Commands: (E)xit (T)ime st(A)rt (S)et (R)aw (L)oop (C)harge (N)ocharge (H)elp");
;  END;

RTC_HELP
	LD	DE,RTC_HELP_MSG
	LD	C,09H			; CP/M write string to console call
	CALL	0005H
	RET


; function RTC_TOP_LOOP
;
; based on following algorithm
;
;  PROCEDURE toploop();
;   var
;     err,i,n,fd  : int;
;  BEGIN
;    putln();
;    help();
;    rtc_reset_on();
;    hold(100);
;    test_bit_delay();
;    rtc_charge_disable();
;    putln("rtc: trickle charger disabled.");
;    loop
;       put("rtc>");
;       gets(line);
;       if line = "exit" then
;          putln("Bye.");
;          exit(0);
;       elsif line = "charge" then
;          putln("Trickle charger enabled.");
;          rtc_charge_enable();
;       elsif line = "nocharge" then
;          putln("Trickle charger disabled.");
;          rtc_charge_disable();
;       elsif line = "start" then
;          rtc_restart();
;          putln("Restarting RTC");
;       elsif line = "t" then
;          rtc_get_time(line);
;          putln("Current time: ",line);
;       elsif line = "raw" then
;          putln();
;          putln("Raw read loop, hit any key to stop...");
;          while read(0,@n,1 + RD_NOWAIT) = 0 loop
;             put(#13,"sec=",hexstr(rtc_read(0))^);
;             put(" min=",hexstr(rtc_read(1))^);
;             hold(500);
;          end loop;
;       elsif line = "loop" then
;          putln();
;          putln("Clock loop, hit any key to stop...");
;          while read(0,@n,1 + RD_NOWAIT) = 0 loop
;             rtc_get_time(line);
;             put(#13,line);
;             hold(200);
;          end loop;
;       elsif line = "set" then
;          putln("Setting RTC time to 96-02-18 19:43:00");
;          rtc_set_now();
;       elsif (line = "help") or (line = "?") then
;          help();
;       elsif length(line) <> 0 then
;          putln("You typed: """,line,"""");
;       end;
;    end loop;
;  END toploop;

RTC_TOP_LOOP:
	LD	DE,CRLF_MSG
	LD	C,09H			; CP/M write string to console call
	CALL	0005H

	CALL	RTC_HELP

	CALL	RTC_RESET_ON

	CALL	RTC_BIT_DELAY
	CALL	RTC_BIT_DELAY
	CALL	RTC_BIT_DELAY

;	CALL	RTC_TEST_BIT_DELAY

	CALL	RTC_CHARGE_DISABLE
	
	LD	DE,RTC_TOP_LOOP1_MSG
	LD	C,09H			; CP/M write string to console call
	CALL	0005H

RTC_TOP_LOOP_1:
	LD	DE,RTC_TOP_LOOP1_PROMPT
	LD	C,09H			; CP/M write string to console call
	CALL	0005H
	
	LD	C,01H			; CP/M console input call
	CALL	0005H

	AND	%01011111		; handle lower case responses to menu

	CP	'E'
	JP	Z,RTC_TOP_LOOP_EXIT

	CP	'C'
	JP	Z,RTC_TOP_LOOP_CHARGE

	CP	'N'
	JP	Z,RTC_TOP_LOOP_NOCHARGE

	CP	'A'
	JP	Z,RTC_TOP_LOOP_START

	CP	'T'
	JP	Z,RTC_TOP_LOOP_TIME

	CP	'R'
	JP	Z,RTC_TOP_LOOP_RAW

	CP	'L'
	JP	Z,RTC_TOP_LOOP_LOOP

	CP	'H'
	JP	Z,RTC_TOP_LOOP_HELP

	CP	'D'
	JP	Z,RTC_TOP_LOOP_DELAY

	CP	'S'
	JP	Z,RTC_TOP_LOOP_SET

	LD	DE,RTC_TOP_LOOP1_OTHER1
	LD	C,09H			; CP/M write string to console call
	CALL	0005H

	LD	E,A
	LD	C,02H			; CP/M Console output call
	CALL	0005H

	LD	DE,RTC_TOP_LOOP1_OTHER2
	LD	C,09H			; CP/M write string to console call
	CALL	0005H
	
	JP	RTC_TOP_LOOP_1

RTC_TOP_LOOP_EXIT:
	RET

RTC_TOP_LOOP_CHARGE:
	LD	DE,RTC_TOP_LOOP1_CHARGE
	LD	C,09H			; CP/M write string to console call
	CALL	0005H
	CALL	RTC_CHARGE_ENABLE
	JP	RTC_TOP_LOOP_1

RTC_TOP_LOOP_NOCHARGE:
	LD	DE,RTC_TOP_LOOP1_NOCHARGE
	LD	C,09H			; CP/M write string to console call
	CALL	0005H
	CALL	RTC_CHARGE_DISABLE
	JP	RTC_TOP_LOOP_1

RTC_TOP_LOOP_START:
	LD	DE,RTC_TOP_LOOP1_START
	LD	C,09H			; CP/M write string to console call
	CALL	0005H
	CALL	RTC_RESTART
	JP	RTC_TOP_LOOP_1

RTC_TOP_LOOP_TIME:
	LD	DE,RTC_TOP_LOOP1_TIME
	LD	C,09H			; CP/M write string to console call
	CALL	0005H
	CALL	RTC_GET_TIME
	LD	DE,RTC_PRINT_BUFFER
	LD	C,09H			; CP/M write string to console call
	CALL	0005H
	JP	RTC_TOP_LOOP_1

RTC_TOP_LOOP_RAW:
	LD	DE,RTC_TOP_LOOP1_RAW
	LD	C,09H			; CP/M write string to console call
	CALL	0005H
RTC_TOP_LOOP_RAW1:

;	{ Read seconds }
	LD	D,$00			; seconds register in DS1302
	CALL	RTC_READ		; read value from DS1302, value is in Reg C

	; digit 16
	LD	A,C			; put value output in Reg C into accumulator
	RLC	A
	RLC	A
	RLC	A
	RLC	A
	AND	$07
	ADD	A,'0'
	LD	(RTC_PRINT_BUFFER+15),A

	; digit 17
	LD	A,C			; put value output in Reg C into accumulator
	AND	$0F
	ADD	A,'0'
	LD	(RTC_PRINT_BUFFER+16),A

;	{ Read minutes }

	LD	D,$01			; minutes register in DS1302
	CALL	RTC_READ		; read value from DS1302, value is in Reg C

	; digit 13
	LD	A,C			; put value output in Reg C into accumulator
	RLC	A
	RLC	A
	RLC	A
	RLC	A
	AND	$07
	ADD	A,'0'
	LD	(RTC_PRINT_BUFFER+12),A

	; digit 14
	LD	A,C			; put value output in Reg C into accumulator
	AND	$0F
	ADD	A,'0'
	LD	(RTC_PRINT_BUFFER+13),A

	; digit 15
	LD	A,':'
	LD	(RTC_PRINT_BUFFER+14),A

	; digits 1-12 and 18-20 are spaces
	LD	A,' '			; space
	LD	(RTC_PRINT_BUFFER+19),A
	LD	(RTC_PRINT_BUFFER+18),A
	LD	(RTC_PRINT_BUFFER+17),A
	LD	(RTC_PRINT_BUFFER+11),A
	LD	(RTC_PRINT_BUFFER+10),A
	LD	(RTC_PRINT_BUFFER+09),A
	LD	(RTC_PRINT_BUFFER+08),A
	LD	(RTC_PRINT_BUFFER+07),A
	LD	(RTC_PRINT_BUFFER+06),A
	LD	(RTC_PRINT_BUFFER+05),A
	LD	(RTC_PRINT_BUFFER+04),A
	LD	(RTC_PRINT_BUFFER+03),A
	LD	(RTC_PRINT_BUFFER+02),A
	LD	(RTC_PRINT_BUFFER+01),A
	LD	(RTC_PRINT_BUFFER+00),A

	LD	DE,RTC_PRINT_BUFFER
	LD	C,09H			; CP/M write string to console call
	CALL	0005H

	LD	C,01H			; CP/M console input call
	CALL	0005H

	CP	' '			; space
	JP	Z,RTC_TOP_LOOP_RAW1

	JP	RTC_TOP_LOOP_1

RTC_TOP_LOOP_LOOP:
	LD	DE,RTC_TOP_LOOP1_LOOP
	LD	C,09H			; CP/M write string to console call
	CALL	0005H

RTC_TOP_LOOP_LOOP1:
	CALL	RTC_GET_TIME

	LD	DE,RTC_PRINT_BUFFER
	LD	C,09H			; CP/M write string to console call
	CALL	0005H

	LD	C,01H			; CP/M console input call
	CALL	0005H

	CP	' '
	JP	Z,RTC_TOP_LOOP_LOOP1	

	JP	RTC_TOP_LOOP_1

RTC_TOP_LOOP_SET:
	LD	DE,RTC_TOP_LOOP1_SET
	LD	C,09H			; CP/M write string to console call
	CALL	0005H
	CALL	RTC_SET_NOW
	JP	RTC_TOP_LOOP_1

RTC_TOP_LOOP_DELAY:
	CALL	RTC_TEST_BIT_DELAY
	JP	RTC_TOP_LOOP_1

RTC_TOP_LOOP_HELP:
	CALL	RTC_HELP
	JP	RTC_TOP_LOOP_1

;
; Text Strings
;

MSG:
	.TEXT	"Start Real Time Clock Program"
	.DB	0Ah, 0Dh		; line feed and carriage return
	.DB	"$"			; Line terminator

CRLF_MSG:
	.DB	0Ah, 0Dh		; line feed and carriage return
	.DB	"$"			; Line terminator

TESTING_BIT_DELAY_MSG:
	.DB	0Ah, 0Dh		; line feed and carriage return
	.TEXT	"Testing bit delay.  Successful test is approximately 43 seconds."
	.DB	0Ah, 0Dh		; line feed and carriage return
	.TEXT	"Start clock and press space bar to continue."
	.DB	0Ah, 0Dh		; line feed and carriage return
	.DB	"$"			; Line terminator

TESTING_BIT_DELAY_OVER:
	.DB	0Ah, 0Dh		; line feed and carriage return
	.TEXT	"Test complete.  Stop clock."
	.DB	0Ah, 0Dh		; line feed and carriage return
	.DB	"$"			; Line terminator

RTC_HELP_MSG:
	.DB	0Ah, 0Dh		; line feed and carriage return
	.TEXT	"RTC: Version 1.0"
	.DB	0Ah, 0Dh		; line feed and carriage return
	.TEXT	"RTC: Commands: (E)xit (T)ime st(A)rt (S)et (R)aw (L)oop (C)harge (N)ocharge (D)elay (H)elp"
	.DB	0Ah, 0Dh		; line feed and carriage return
	.DB	"$"			; Line terminator

RTC_TOP_LOOP1_MSG:
	.DB	0Ah, 0Dh		; line feed and carriage return
	.TEXT	"RTC: trickle charger disabled."
	.DB	0Ah, 0Dh		; line feed and carriage return
	.DB	"$"			; Line terminator

RTC_TOP_LOOP1_PROMPT:
	.DB	0Ah, 0Dh		; line feed and carriage return
	.TEXT	"RTC>"
	.DB	"$"			; Line terminator

RTC_TOP_LOOP1_CHARGE:
	.DB	0Ah, 0Dh		; line feed and carriage return
	.TEXT	"Trickle charger enabled."
	.DB	0Ah, 0Dh		; line feed and carriage return
	.DB	"$"			; Line terminator

RTC_TOP_LOOP1_NOCHARGE:
	.DB	0Ah, 0Dh		; line feed and carriage return
	.TEXT	"Trickle charger disabled."
	.DB	0Ah, 0Dh		; line feed and carriage return
	.DB	"$"			; Line terminator

RTC_TOP_LOOP1_START:
	.DB	0Ah, 0Dh		; line feed and carriage return
	.TEXT	"Restarting RTC."
	.DB	0Ah, 0Dh		; line feed and carriage return
	.DB	"$"			; Line terminator

RTC_TOP_LOOP1_TIME:
	.DB	0Ah, 0Dh		; line feed and carriage return
	.TEXT	"Current time: "
	.DB	"$"			; Line terminator

RTC_TOP_LOOP1_RAW:
	.DB	0Ah, 0Dh		; line feed and carriage return
	.TEXT	"Raw read Loop.  Press SPACE BAR for next or any other key to stop."
	.DB	0Ah, 0Dh		; line feed and carriage return
	.DB	"$"			; Line terminator

RTC_TOP_LOOP1_LOOP:
	.DB	0Ah, 0Dh		; line feed and carriage return
	.TEXT	"Clock Loop.  Press SPACE BAR for next or any other key to stop."
	.DB	0Ah, 0Dh		; line feed and carriage return
	.DB	"$"			; Line terminator

RTC_TOP_LOOP1_SET:
	.DB	0Ah, 0Dh		; line feed and carriage return
	.TEXT	"Setting RTC time to 07-02-23 19:45:00."
	.DB	0Ah, 0Dh		; line feed and carriage return
	.DB	"$"			; Line terminator

RTC_TOP_LOOP1_OTHER1:
	.DB	0Ah, 0Dh		; line feed and carriage return
	.TEXT	"YOU TYPED: "
	.DB	"$"			; Line terminator

RTC_TOP_LOOP1_OTHER2:
	.DB	0Ah, 0Dh		; line feed and carriage return
	.DB	"$"			; Line terminator

RTC_PRINT_BUFFER:
	.DS	20			; Buffer for formatted date & time to print
	.DB	0Ah, 0Dh		; line feed and carriage return
	.DB	"$"			; line terminator

;
; Generic FOR-NEXT loop algorithm
;
;	LD	A,$00			; set A=0 index counter of FOR loop
;FOR_LOOP:
;	PUSH	AF			; save accumulator as it is the index counter in FOR loop
;	{ contents of FOR loop here }	; setup RTC with RST and RD high, SCLK low
;	POP	AF			; recover accumulator as it is the index counter in FOR loop
;	INC	A			; increment A in FOR loop (A=A+1)
;	CP	$08			; is A < $08 ?
;	JP	NZ,FOR_LOOP		; No, do FOR loop again
;	RET				; Yes, end function and return.  Read RTC value is in C

	.ORG	$08ff
	.db	$00

.end


